package com.myjob.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.myjob.DTO.SecurityUser;
import com.myjob.common.MessageConstant;
import com.myjob.entity.*;
import com.myjob.enums.RedisEnums;
import com.myjob.utils.RedisUtil;
import com.myjob.utils.UtilValidate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author chenyuan
 * @date 2021/3/17 20:45
 */
@Service
@Slf4j
public class SpringDataUserDetailsServiceImpl implements UserDetailsService {
    @Autowired
    private ResourceService resourceService;
    @Autowired
    private RoleResourceRelService roleResourceRelService;
    @Autowired
    private RoleService roleService;
    @Autowired
    private UserService userService;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private PasswordEncoder passwordEncoder;

    //根据 账号查询用户信息
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Boolean isEmailCode = false;
        if (username.contains("##")) {
            username = username.replace("##","").trim();
            isEmailCode = true;
        }

        //通过传来的用户名查询用户信息
        User user = this.getUserByUsername(username);

        if(UtilValidate.isEmpty(user)){
            log.info("<< AUTH >> --- 传来的 UserName："+username+" , 查无此人,抛出异常");
            //如果用户查不到，返回null，由provider来抛出异常
            throw new UsernameNotFoundException(MessageConstant.USERNAME_PASSWORD_ERROR);
        }
        log.info("<< AUTH >> --- 传来的 UserName："+username+" , 查询到的结果为："+user.toString());
        this.selectAllRoleResourceRel();
        this.getRoleCode(user);
        if (isEmailCode){
            String password = user.getPassword();
            password = passwordEncoder.encode(password);
            user.setPassword(password);
        }
        return new SecurityUser(user);
    }

    /**
     * 根据账号查询用户信息
     */
    public User getUserByUsername(String username){
        User user = userService.selectByPhoneOrEmil(username);
        if (UtilValidate.isNotEmpty(user)) {
            return user;
        }
        return null;
    }

    public void selectAllRoleResourceRel(){
        // 查询出所有的资源信息
        List<RoleResourceRel> roleResources = roleResourceRelService.list();
        List<Resource> resources = resourceService.list();

        HashMap<String, Resource> resourceMap = new HashMap<>();
        for (Resource resource : resources) {
            resourceMap.put(resource.getResourceId(), resource);
        }

        // 将 resourceUrl 和 roleId 对应关系存进 Redis
        setUrlAndRoleIdsRelToRedis(roleResources, resourceMap);
        // 将 roleId 和 resourceCode 对应关系存进 Redis
        setRoleIdAndResourceCodeToRedis(roleResources, resourceMap);
    }

    public void getRoleCode(User user){
        if (UtilValidate.isEmpty(user)) {
            return;
        }

        QueryWrapper<Role> wrapper = new QueryWrapper<>();
        wrapper.eq("name", user.getRole());
        Role one = roleService.getOne(wrapper);
        if (UtilValidate.isEmpty(one)) {
            return;
        }else {
            user.setRoleCode(String.valueOf(one.getId()));
        }
    }

    //@Async
    public Boolean setUrlAndRoleIdsRelToRedis(List<RoleResourceRel> roleResources,
                                              HashMap<String, Resource> resourceMap){

        Map<Object, Object> roleResourceRel = redisUtil.hmget(RedisEnums.KEY_IN_REDIS.AUTH_RESOURCE_URL_ROLE_IDS_CEL.getValue());
        if (UtilValidate.isNotEmpty(roleResourceRel)) {
            log.info("<< AUTH >> --- resourceUrl 和 roleId 关系信息已经存在直接返回");
            return true;
        }

        HashMap<String, List<String>> roleResourceMap = new HashMap<>();
        for (RoleResourceRel roleResource : roleResources) {
            Long resourceId = roleResource.getResourceId();
            Resource resource = resourceMap.get(String.valueOf(resourceId));
            String key = resource.getResourceMethod() + "-" + resource.getResourceUrl();
            List<String> list = roleResourceMap.get(key);
            if (UtilValidate.isEmpty(list)){
                list = new ArrayList<>();
            }
            list.add("["+roleResource.getRoleId()+"]");
            roleResourceMap.put(key, list);
        }
        log.info("<< AUTH >> --- 正在将 (资源URL, 角色ID) 关系信息存入 Redis");
        boolean hmset = redisUtil.hmset(RedisEnums.KEY_IN_REDIS.AUTH_RESOURCE_URL_ROLE_IDS_CEL.getValue(), roleResourceMap, 12, TimeUnit.HOURS);
        if (!hmset){
            // 不成功重试一次
            log.info("<< AUTH >> --- 出错, 进行重试");
            hmset = redisUtil.hmset(RedisEnums.KEY_IN_REDIS.AUTH_RESOURCE_URL_ROLE_IDS_CEL.getValue(), roleResourceMap, 12, TimeUnit.HOURS);
        }
        log.info("<< AUTH >> --- 成功将 (资源URL, 角色ID) 关系信息存入 Redis");
        return hmset;
    }

    //@Async
    public Boolean setRoleIdAndResourceCodeToRedis(List<RoleResourceRel> roleResources,
                                                   HashMap<String, Resource> resourceMap){

        Map<Object, Object> roleResourceRel = redisUtil.hmget(RedisEnums.KEY_IN_REDIS.AUTH_ROLE_ID_RESOURCE_CODE_CEL.getValue());
        if (UtilValidate.isNotEmpty(roleResourceRel)) {
            log.info("<< AUTH >> --- roleId 和 resourceCode 关系信息已经存在直接返回");
            return true;
        }

        HashMap<String, List<String>> roleResourceMap = new HashMap<>();
        for (RoleResourceRel roleResource : roleResources) {
            String key = String.valueOf(roleResource.getRoleId());
            Long resourceId = roleResource.getResourceId();
            Resource resource = resourceMap.get(String.valueOf(resourceId));
            List<String> resources = roleResourceMap.get(key);

            if (UtilValidate.isEmpty(resources)) {
                resources = new ArrayList<>();
            }

            resources.add(resource.getResourceCode());
            roleResourceMap.put(key, resources);
        }
        log.info("<< AUTH >> --- 正在将 (角色ID, 资源Code) 关系信息存入 Redis");
        boolean hmset = redisUtil.hmset(RedisEnums.KEY_IN_REDIS.AUTH_ROLE_ID_RESOURCE_CODE_CEL.getValue(), roleResourceMap, 12, TimeUnit.HOURS);
        if (!hmset){
            // 不成功重试一次
            log.info("<< AUTH >> --- 出错, 进行重试");
            hmset = redisUtil.hmset(RedisEnums.KEY_IN_REDIS.AUTH_ROLE_ID_RESOURCE_CODE_CEL.getValue(), roleResourceMap, 12, TimeUnit.HOURS);
        }
        log.info("<< AUTH >> --- 成功将 (角色ID, 资源Code) 关系信息存入 Redis");
        return hmset;
    }
}
